#include "Application.h"

using namespace Graphics;
using namespace std;
using namespace boost;
using namespace libPortal;
using namespace libPortal::DoomIII;

Application::Application(const std::string &filename) :
  cam(700,700,700)
{
  if(!d3map.load(filename))
    cout<<"Couldn't load map '"<<filename<<"'"<<endl;
  std::vector<DoomIIIBrush3D>::const_iterator iter, end;
  end = d3map.end();
  Matrix4d brushTransform;
  for(iter=d3map.begin();iter!=end;iter++)
    {
      BOOST_FOREACH(shared_ptr<BSPPlane> pPtr, iter->planes)
	addPlaneToRender(*pPtr);
      if(isRectangular(*iter, brushTransform))
	{
	  unitCubes.push_back(brushTransform);
	}
    }
}

Application::~Application()
{

}


void Application::InitGL() const
{
  //Some standard opengl init stuff.
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);
  glClearDepth(1.0f);
  glDepthFunc(GL_LEQUAL);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  glEnable(GL_TEXTURE_2D);
}

void Application::Resize(unsigned int w, unsigned int h, bool ortho) const
{
  if(h == 0)
    h = 1;

  float ratio = 1.0f* w / h;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glViewport(0, 0, w, h);

  if(!ortho)
    gluPerspective(45,ratio,10,3000);
  else
    gluOrtho2D(0, w, h, 0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void Application::Render() const
{
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  cam.setModelView();
  glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
  //drawUnitCube();
  std::for_each(planes.begin(), planes.end(), &drawPlane);

  BOOST_FOREACH(Matrix4d m, unitCubes)
    {
      glPushMatrix();
      glMultMatrixd(&m.ml[0]);
      drawUnitCube();
      glPopMatrix();
    }

  glFinish();
  glFlush();
}

namespace Graphics {
  void drawUnitCube()
  {
    glColor3f(0.0f, 1.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex3d(0.5f, 0.5f, 0.5f);
    glVertex3d(-0.5f, 0.5f, 0.5f);
    glVertex3d(0.5f, -0.5f, 0.5f);
    glVertex3d(-0.5f, -0.5f, 0.5f);
    glVertex3d(0.5f, 0.5f, 0.5f);
    glVertex3d(0.5f, -0.5f, 0.5f);
    glVertex3d(-0.5f, 0.5f, 0.5f);
    glVertex3d(-0.5f, -0.5f, 0.5f);

    glVertex3d(0.5f, 0.5f, -0.5f);
    glVertex3d(-0.5f, 0.5f, -0.5f);
    glVertex3d(0.5f, -0.5f, -0.5f);
    glVertex3d(-0.5f, -0.5f, -0.5f);
    glVertex3d(0.5f, 0.5f, -0.5f);
    glVertex3d(0.5f, -0.5f, -0.5f);
    glVertex3d(-0.5f, 0.5f, -0.5f);
    glVertex3d(-0.5f, -0.5f, -0.5f);

    glVertex3d(0.5f, 0.5f, 0.5f);
    glVertex3d(0.5f, 0.5f, -0.5f);
    glVertex3d(0.5f, -0.5f, 0.5f);
    glVertex3d(0.5f, -0.5f, -0.5f);
    glVertex3d(-0.5f, 0.5f, 0.5f);
    glVertex3d(-0.5f, 0.5f, -0.5f);
    glVertex3d(-0.5f, -0.5f, 0.5f);
    glVertex3d(-0.5f, -0.5f, -0.5f);
    glEnd();
    glColor3f(1.0f, 1.0f, 1.0f);
  }

  void drawPlane(const PlaneRenderData &d)
  {
    if(d.tex!=NULL)
      glBindTexture(GL_TEXTURE_2D, d.tex->ID());
    else
      glBindTexture(GL_TEXTURE_2D, 0);
    glBegin(GL_TRIANGLES);
    glNormal3d(d.normal.x, d.normal.y, d.normal.z);
    for(unsigned int i = 0;i<d.points.size();i++)
      {
	glTexCoord2d(d.texCoords[i].x, d.texCoords[i].y);
	glVertex3d(d.points[i].x, d.points[i].y, d.points[i].z);
      }
    glEnd();
  }
}

void Application::Update(int milliseconds, Uint8 *keyStates, int dx, int dy)
{
  if(keyStates[SDLK_UP])
    cam.MoveForwards(-milliseconds);
  if(keyStates[SDLK_DOWN])
    cam.MoveForwards(milliseconds);
  if(keyStates[SDLK_LEFT])
    cam.StrafeRight(-milliseconds);
  if(keyStates[SDLK_RIGHT])
      cam.StrafeRight(milliseconds);

  cam.RotateX(-((double)dy)/10.0);
  cam.RotateY(-((double)dx)/10.0);
}

void Application::addPlaneToRender(const BSPPlane &p)
{
  assert(p.verts.size()==p.texCoords.size());
  if(p.textureFileName.find("textures/common")==0 ||
     p.textureFileName.find("textures/editor")==0)
    return;
  PlaneRenderData newData;
  newData.points = p.verts;
  newData.texCoords = p.texCoords;
  newData.normal = p.normal;
  unsigned int notused;
  newData.tex = texMan.Load(p.textureFileName.c_str(), notused, NULL);
  if(newData.tex==NULL && p.textureFileName.size()>=5)
    {
      string with_d(p.textureFileName);
      with_d.insert(with_d.size()-4, "_d");
      cout<<"Trying '"<<with_d<<"'"<<endl;
      newData.tex = texMan.Load(p.textureFileName.c_str(), notused, NULL);
    }
  if(newData.tex == NULL)
    cout<<"Could not find '"<<p.textureFileName<<"'"<<endl;
  planes.push_back(newData);
}
